/*
 * aligned_mem.h
 *
 *  Created on: May 14, 2013
 *      Author: cforster
 *
 *  Code from libcvd: cvd/internal/aligned_mem.h
 *  https://github.com/edrosten/libcvd
 *  Licence: LGPL 2.1
 */

#ifndef VIKIT_ALIGNED_MEM_H_
#define VIKIT_ALIGNED_MEM_H_

#include <string.h>  // memset
#include <cassert>
#include <cstdlib>
#include <string>  // memset

namespace vk {
namespace aligned_mem {

/// Check if the pointer is aligned to the specified byte granularity
inline bool is_aligned8(const void* ptr) {
  return ((reinterpret_cast<size_t>(ptr)) & 0x7) == 0;
}

inline bool is_aligned16(const void* ptr) {
  return ((reinterpret_cast<size_t>(ptr)) & 0xF) == 0;
}

template <class T, int N = 20>
struct placement_delete {
  enum { Size = (1 << N) };

  struct Array {
    T data[Size];
  };

  static inline void destruct(T* buf) {
    (*(Array*)buf).~Array();
  }

  static inline void free(T* buf, size_t M) {
    if (M >= Size) {
      placement_delete<T, N>::free(buf + Size, M - Size);
      placement_delete<T, N>::destruct(buf);
    } else {
      placement_delete<T, N - 1>::free(buf, M);
    }
  }
};

template <class T>
struct placement_delete<T, -1> {
  static inline void free(T*, size_t) {}
};

inline void* aligned_alloc(size_t count, size_t alignment) {
  void* mem = NULL;
  assert(posix_memalign(&mem, alignment, count) == 0);
  return mem;
}

inline void aligned_free(void* memory) {
  free(memory);
}

template <class T>
inline T* aligned_alloc(size_t count, size_t alignment) {
  void* data = aligned_alloc(sizeof(T) * count, alignment);
  return new (data) T[count];
}

template <class T>
inline void aligned_free(T* memory, size_t count) {
  placement_delete<T>::free(memory, count);
  aligned_free(memory);
}

template <class T>
inline void memfill(T* data, int n, const T val) {
  T* de = data + n;
  for (; data < de; data++)
    *data = val;
}

template <>
inline void memfill(unsigned char* data, int n, const unsigned char val) {
  memset(data, val, n);
}

template <>
inline void memfill(signed char* data, int n, const signed char val) {
  memset(data, val, n);
}

template <>
inline void memfill(char* data, int n, const char val) {
  memset(data, val, n);
}

template <class T, int N>
struct AlignedMem {
  T* mem;
  size_t count;
  AlignedMem(size_t c) : count(c) {
    mem = aligned_alloc<T>(count, N);
  }
  ~AlignedMem() {
    aligned_free<T>(mem, count);
  }
  T* data() {
    return mem;
  }
  const T* data() const {
    return mem;
  }
};

}  // namespace aligned_mem
}  // namespace vk

#endif  // VIKIT_ALIGNED_MEM_H_
